package com.dimples.security.handler;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dimples.common.constant.DimplesConst;
import com.dimples.common.entity.LoginDetails;
import com.dimples.common.entity.SecurityUser;
import com.dimples.common.enums.CodeMsgEnum;
import com.dimples.common.properties.DimplesProperties;
import com.dimples.common.service.DimplesJwtTokenServiceImpl;
import com.dimples.common.util.HttpContextUtil;
import com.dimples.security.entity.SecurityUserDetails;
import com.dimples.system.entity.Logging;
import com.dimples.system.entity.SysDept;
import com.dimples.system.entity.SysRole;
import com.dimples.system.entity.SysUser;
import com.dimples.system.service.LoggingService;
import com.dimples.system.service.SysDeptService;
import com.dimples.system.service.SysRoleService;
import com.dimples.system.service.SysUserService;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 认证通过处理器
 *
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2021/6/26
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
public class DimplesAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private final DimplesJwtTokenServiceImpl jwtTokenHelper;
    private final DimplesProperties properties;
    private final SysRoleService roleService;
    private final LoggingService loggingService;
    private final SysDeptService deptService;

    @Resource
    private SysUserService userService;

    /**
     * 自定义登陆逻辑
     *
     * @param request        HttpServletRequest
     * @param response       HttpServletResponse
     * @param authentication Authentication参数既包含了认证请求的一些信息，比如IP，请求的SessionId等，也包含了用户信息，即前面提到的SecurityUser对象
     * @throws IOException IOException
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails();

        SecurityUser securityUser = new SecurityUser();
        if (authentication.getPrincipal() instanceof SecurityUserDetails) {
            SecurityUserDetails securityUserDetails = (SecurityUserDetails) authentication.getPrincipal();
            securityUserDetails.setRemoteAddress(details.getRemoteAddress());
            BeanUtil.copyProperties(securityUserDetails, securityUser);
            Set<String> authorities = securityUserDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
            securityUser.setAuthorities(authorities);
            // 查询用户角色
            List<SysRole> sysRoles = this.roleService.findRoleByUserId(securityUser.getUserId());
            securityUser.setRoleCodes(sysRoles.stream().map(SysRole::getRoleCode).collect(Collectors.toList()));
            // 查询科室
            SysDept dept = this.deptService.getById(securityUser.getDeptId());
            if (ObjectUtil.isNotEmpty(dept)) {
                securityUser.setDeptId(dept.getDeptId());
                securityUser.setDeptName(dept.getDeptName());
            }
        }

        securityUser.setExpireTime(DateUtil.formatDateTime(DateUtil.offsetMinute(DateUtil.parseDateTime(DateUtil.now()), properties.getSecurity().getToken().getExpiration())));

        // 更新登陆时间
        SysUser user = new SysUser();
        user.setLastLoginTime(new Date());
        this.userService.update(user, Wrappers.lambdaUpdate(SysUser.class).eq(SysUser::getUsername, securityUser.getUsername()));
        // 存储登陆信息
        securityUser.getLoginDetails().setIpAddr(HttpContextUtil.getIp());
        Logging logging = this.loggingService.savaLoginLog(securityUser);

        response.setContentType(DimplesConst.JSON_UTF8);
        // 生成token 并返回
        JSONObject result = new JSONObject();
        result.set("code", CodeMsgEnum.LOGIN_SUCCESS.getCode());
        result.set("msg", CodeMsgEnum.LOGIN_SUCCESS.getMessage());
        String token = jwtTokenHelper.createToken(securityUser);
        result.set("token", token);
        result.set("user", securityUser);

        // 刷新token
        LoginDetails loginDetails = new LoginDetails();
        Claims claims = jwtTokenHelper.getClaimsFromToken(token);
        loginDetails.setTokenId(claims.getId());
        loginDetails.setIpAddr(logging.getOperateIp());
        loginDetails.setLocation(logging.getOperateAddr());
        loginDetails.setBrowser(logging.getBrowser());
        loginDetails.setOs(logging.getSystemOs());
        securityUser.setLoginDetails(loginDetails);
        jwtTokenHelper.cacheToken(jwtTokenHelper.getTokenKey(jwtTokenHelper.getClaimsFromToken(jwtTokenHelper.parseToken(token)).getId()), JSONUtil.parseObj(securityUser));

        response.getWriter().write(JSONUtil.toJsonStr(result));
    }
}
